package com.simpleplayer.views.videoview;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Build;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.OnScaleGestureListener;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.WindowManager;
import android.widget.ImageView;

import com.simpleplayer.R;
import com.simpleplayer.MediaPlayer.SimplePlayer;
import com.simpleplayer.services.LogUtil;

@SuppressLint("ViewConstructor")
class FloatWindowVideoView extends VideoViewInternal implements OnGestureListener, OnScaleGestureListener {
    private static final String TAG = "FloatWindowVideoView";

    private static final int HIDE_CONTROLS_DELAY = 4000; // 单位: 毫秒
    private static final int DEFAULT_WINDOW_HEIGHT = 320;
    private static final int MAX_WINDOW_HEIGHT = 640;
    private static final int MIN_WINDOW_HEIGHT = 240;
    private static final int DEFAULT_WINDOW_WIDTH = 640;
    private static final int MAX_WINDOW_WIDTH = 1280;
    private static final int MIN_WINDOW_WIDTH = 480;

    private ImageView mReturnButton;
    private ImageView mBackwardButton;
    private ImageView mCenterPlayButton;
    private ImageView mForwardButton;
    private ViewGroup mContentContainer;
    private ViewGroup mControlsContainer;
    private ViewGroup mFloatContainer;

    private final Context mContext;
    private final VideoView mParent;
    private final SimplePlayer mPlayer;
    private Runnable mHideControlsTask;

    private WindowManager mWindowManager;
    private WindowManager.LayoutParams mWindowLayoutParams;
    private final GestureDetector mGestureDetector;
    private final ScaleGestureDetector mScaleGestureDetector;
    private int mScreenHeight;
    private int mScreenWidth;
    private int mDownRawX; // 移动window时的初始位置
    private int mDownRawY;

    private final SimplePlayerListener mPlayerEventListener;

    public FloatWindowVideoView(Context context, SimplePlayer player, VideoView parent) {
        super(context);
        mContext = context;
        mParent = parent;
        mPlayer = player;
        mPlayerEventListener = new SimplePlayerListener();

        mGestureDetector = new GestureDetector(mContext,this);
        mScaleGestureDetector = new ScaleGestureDetector(mContext, this);
        mScaleGestureDetector.setQuickScaleEnabled(false);

        initViews();
        initCommon();
    }

    private void initViews() {
        View.inflate(mContext, R.layout.float_video_view, this);

        mReturnButton = findViewById(R.id.return_button);
        mBackwardButton = findViewById(R.id.backward_button);
        mCenterPlayButton = findViewById(R.id.center_play_button);
        mForwardButton = findViewById(R.id.forward_button);
        mContentContainer = findViewById(R.id.content_container);
        mControlsContainer = findViewById(R.id.controls_container);
        mFloatContainer = findViewById(R.id.float_container);

        OnControlsClickListener clickControlsListener = new OnControlsClickListener();
        mReturnButton.setOnClickListener(clickControlsListener);
        mBackwardButton.setOnClickListener(clickControlsListener);
        mCenterPlayButton.setOnClickListener(clickControlsListener);
        mForwardButton.setOnClickListener(clickControlsListener);
        mFloatContainer.setOnClickListener(new OnFloatViewsClickListener());
        mFloatContainer.setOnTouchListener(new OnFloatViewsTouchListener());
    }

    @SuppressLint("RtlHardcoded")
    private void initCommon() {
        // 初始化自身布局
        this.setLayoutParams(
            new ViewGroup.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT
            )
        );

        // 初始化window
        mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        mScreenHeight = mWindowManager.getDefaultDisplay().getHeight();
        mScreenWidth = mWindowManager.getDefaultDisplay().getWidth();
        mWindowLayoutParams = new WindowManager.LayoutParams();
        mWindowLayoutParams.width = mPlayer.getVideoWidth();
        mWindowLayoutParams.height = mPlayer.getVideoHeight();
        fixWindowSize();
        mWindowLayoutParams.x = (mScreenWidth - mWindowLayoutParams.width) / 2;
        mWindowLayoutParams.y = (mScreenHeight - mWindowLayoutParams.height) / 4;
        mWindowLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;
        mWindowLayoutParams.format= PixelFormat.RGBA_8888;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            mWindowLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
        } else {
            mWindowLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
        }
        mWindowLayoutParams.flags |= WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE |
                WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON |
                WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;

        mPlayer.addListener(mPlayerEventListener);
    }

    private void fixWindowSize() {
        if (mWindowLayoutParams.width <= 0 || mWindowLayoutParams.height <= 0) {
            mWindowLayoutParams.width = DEFAULT_WINDOW_WIDTH;
            mWindowLayoutParams.height = DEFAULT_WINDOW_HEIGHT;
        }

        float ratio = (float)mWindowLayoutParams.width / mWindowLayoutParams.height;
        mWindowLayoutParams.width = Math.min(mWindowLayoutParams.width, MAX_WINDOW_WIDTH);
        mWindowLayoutParams.width = Math.max(mWindowLayoutParams.width, MIN_WINDOW_WIDTH);
        mWindowLayoutParams.height = (int)(mWindowLayoutParams.width * ratio);
        mWindowLayoutParams.height = Math.min(mWindowLayoutParams.height, MAX_WINDOW_HEIGHT);
        mWindowLayoutParams.height = Math.max(mWindowLayoutParams.height, MIN_WINDOW_HEIGHT);

        LogUtil.d(TAG, "fixWindowParams， ratio: " + ratio + ", width: " + mWindowLayoutParams.width +
            ", height: " + mWindowLayoutParams.height);
    }

    private void updateViewLayoutPosition(int x, int y) {
        LogUtil.v(TAG, "updateViewLayoutPosition to (" + x + ", " + y + ")");
        mWindowLayoutParams.x = Math.max(0, x);
        mWindowLayoutParams.y = Math.max(0, y);
        updateViewLayout();
        requestLayout();
    }

    private void updateViewLayout() {
        mWindowManager.updateViewLayout(this, mWindowLayoutParams);
        LogUtil.v(TAG, "updateViewLayout (" + mWindowLayoutParams.x + ", "
                + mWindowLayoutParams.y + ", "
                + mWindowLayoutParams.width + ", "
                + mWindowLayoutParams.height + ")");
    }

    private void resetWindowPosition() {
        mScreenHeight = mWindowManager.getDefaultDisplay().getHeight();
        mScreenWidth = mWindowManager.getDefaultDisplay().getWidth();
        mWindowLayoutParams.width = mPlayer.getVideoWidth();
        mWindowLayoutParams.height = mPlayer.getVideoHeight();
        fixWindowSize();
        mWindowLayoutParams.x = (mScreenWidth - mWindowLayoutParams.width) / 2;
        mWindowLayoutParams.y = (mScreenHeight - mWindowLayoutParams.height) / 4;
        mWindowManager.updateViewLayout(this, mWindowLayoutParams);
    }

    @Override
    public void setContentView(View contentView) {
        if (contentView == null) return;
        // 将view从其父容器移除
        ViewParent parent = contentView.getParent();
        if (parent instanceof ViewGroup) {
            ((ViewGroup)parent).removeView(contentView);
        }

        // 替换展示的view
        if (mContentContainer.getChildCount() != 0) {
            mContentContainer.removeAllViews();
        }
        mContentContainer.addView(contentView);

        mWindowManager.addView(this, mWindowLayoutParams);
        updateViewLayout();
    }

    @Override
    public void onShow() {
        resetWindowPosition();
    }

    @Override
    public void onHide() {
        mPlayer.removeListener(mPlayerEventListener);
        mWindowManager.removeView(this);
    }

    //----------------------------------------------------------------------------------------------
    // OnGestureListener implementation
    @Override
    public boolean onDown(MotionEvent e) {
        LogUtil.i(TAG, "onDown " + e);
        mDownRawX = mWindowLayoutParams.x;
        mDownRawY = mWindowLayoutParams.y;
        return true;
    }

    @Override
    public void onShowPress(MotionEvent e) {
        LogUtil.i(TAG, "onShowPress " + e);
    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        LogUtil.v(TAG, "onSingleTapUp " + e);
        return false;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
        LogUtil.v(TAG, "onScroll, e1:" + e1 + ", e2:" + e2 + ", distanceX:" + distanceX + ", distanceY:" + distanceY);
        updateViewLayoutPosition(Math.round(mDownRawX + e2.getRawX() - e1.getRawX()),
                Math.round(mDownRawY + e2.getRawY() - e1.getRawY()));
        return true;
    }

    @Override
    public void onLongPress(MotionEvent e) {
        LogUtil.v(TAG, "onLongPress, e:" + e);
    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
        LogUtil.v(TAG, "onScroll, e1:" + e1 + ", e2:" + e2 + ", velocityX:" + velocityX + ", velocityY:" + velocityY);
        return false;
    }

    //----------------------------------------------------------------------------------------------
    // OnScaleGestureListener implementation
    @Override
    public boolean onScale(ScaleGestureDetector detector) {
        LogUtil.v(TAG, "onScale, detector:" + detector);
        return false;
    }

    @Override
    public boolean onScaleBegin(ScaleGestureDetector detector) {
        LogUtil.v(TAG, "onScaleBegin, detector:" + detector);
        return false;
    }

    @Override
    public void onScaleEnd(ScaleGestureDetector detector) {
        LogUtil.i(TAG, "onScaleEnd, detector:" + detector);
    }

    // ---------------------------------------------------------------------------------------------
    // 控件显示/隐藏
    private void showControlsOnce() {
        // 显示全屏控件，5s后自动消失
        LogUtil.i(TAG, "showControlsOnce " + this);
        mControlsContainer.setVisibility(VISIBLE);
        hideControlsDelay();
    }

    private void showControls() {
        // 永久显示全屏控件
        LogUtil.i(TAG, "showControls " + this);
        mControlsContainer.setVisibility(VISIBLE);
        if (mHideControlsTask != null) {
            getHandler().removeCallbacks(mHideControlsTask);
        }
    }

    private void hideControls() {
        mControlsContainer.setVisibility(INVISIBLE);
    }

    private void hideControlsDelay() {
        // 延迟delayMillis(ms)后隐藏控件
        if (mHideControlsTask == null) {
            mHideControlsTask = this::hideControls;
        }
        getHandler().removeCallbacks(mHideControlsTask);
        getHandler().postDelayed(mHideControlsTask, HIDE_CONTROLS_DELAY);
    }
    // ---------------------------------------------------------------------------------------------

    // ---------------------------------------------------------------------------------------------
    // Listener 定义
    private class OnControlsClickListener implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            if (mReturnButton.equals(v)) {
                // 进入小屏
                mParent.switchDisplayModeTo(VideoView.INLINE);
            } else if (mCenterPlayButton.equals(v)) {
                // 播放/暂停
                mPlayer.toggle();
                showControlsOnce();
            } else if (mBackwardButton.equals(v)) {
                mPlayer.seekTo(mPlayer.getCurrentPosition() - 1000);
            } else if (mForwardButton.equals(v)) {
                mPlayer.seekTo(mPlayer.getCurrentPosition() + 1000);
            }
        }
    }

    private class OnFloatViewsClickListener implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            // 单击小屏，显示控件
            showControlsOnce();
        }
    }

    private class OnFloatViewsTouchListener implements View.OnTouchListener {
        @SuppressLint("ClickableViewAccessibility")
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if (event.getAction() == MotionEvent.ACTION_DOWN) {
                // 开始按压，显示控件
                showControls();
            } else if (event.getAction() == MotionEvent.ACTION_UP) {
                // 按压结束，隐藏控件
                hideControlsDelay();
            }
            boolean consume = mGestureDetector.onTouchEvent(event);
            consume |= mScaleGestureDetector.onTouchEvent(event);
            return consume;
        }
    }

    private class SimplePlayerListener implements SimplePlayer.Listener {
        @SuppressLint({"SetTextI18n", "SwitchIntDef"})
        @Override
        public void onStateChanged(int oldState, int newState) {
            switch (newState) {
                case SimplePlayer.PAUSE:
                    mCenterPlayButton.setBackgroundResource(R.drawable.ic_play);
                    setKeepScreenOn(false);
                    break;
                case SimplePlayer.PLAYING:
                    mCenterPlayButton.setBackgroundResource(R.drawable.ic_pause);
                    setKeepScreenOn(true);
                    break;
                case SimplePlayer.STOP:
                    mPlayer.removeListener(SimplePlayerListener.this);
                    break;
                default:
                    break;
            }
        }

        @Override
        public void onTimeChanged(long timeUs) {
        }

        @Override
        public void onBufferingStart() {
        }

        @Override
        public void onBufferingEnd() {
        }
    }
}
